<script setup lang="ts">
import {
  createScene,
  createCamera,
  createRenderer,
  createControls,
  createLabelRenderer,
  initLights,
  renderMap,
  timer,
  onMouseMove1,
  onMouseMove2,
  onMouseMove3,
  onMouseMove4,
  onClick,
  onMouseDown,
  renderFlyLines,
  renderBars,
  renderHighlightBlocks,
  renderScatters,
  renderSlots
} from './methods'

const emit = defineEmits(['block-click', 'bar-highlight', 'scatter-highlight'])

const props = defineProps({
  mapId: {
    type: String,
    default: '3dmap'
  },
  geoData: {
    type: Object,
    default: () => ({})
  },
  barData: {
    type: Array,
    default: null
  },
  scatterData: {
    type: Array,
    default: null
  },
  blockData: {
    type: Array,
    default: null
  },
  mapConfig: {
    type: Object,
    default: () => ({})
  },
  scatterConfig: {
    type: Object,
    default: () => ({})
  },
  barConfig: {
    type: Object,
    default: () => ({})
  },
  blockConfig: {
    type: Object,
    default: () => ({})
  },
  flyLineData: {
    type: Array,
    default: null
  },
  flyLineConfig: {
    type: Object,
    default: () => ({})
  },
  bgData: {
    type: Object,
    default: null
  },
  bgConfig: {
    type: Object,
    default: () => ({})
  },
  mapEnv: {
    type: Object,
    default: () => ({})
  },
  slotConfig: {
    type: Object,
    default: () => ({})
  },
  slotData: {
    type: Array,
    default: () => null
  },
  highlightBlocks: {
    type: Array,
    default: () => null
  }
})

let animateId
let onResize

const handleBlockClick = (data: any) => {
  emit('block-click', data)
}

const handleBarHighlight = (data: any) => {
  emit('bar-highlight', data)
}

const handleScatterHighlight = (data: any) => {
  emit('scatter-highlight', data)
}

const removeAll = () => {
  const root = document.getElementById(props.mapId) as HTMLElement
  root.innerHTML = ''
  cancelAnimationFrame(animateId)
  clearInterval(timer[props.mapId])
  window.removeEventListener('mousemove', onMouseMove1[props.mapId])
  window.removeEventListener('mousemove', onMouseMove2[props.mapId])
  window.removeEventListener('mousemove', onMouseMove3[props.mapId])
  window.removeEventListener('mousemove', onMouseMove4[props.mapId])
  window.removeEventListener('mouseup', onClick[props.mapId])
  window.removeEventListener('mousedown', onMouseDown[props.mapId])
  window.removeEventListener('resize', onResize)
}

const init = () => {
  // 清空所有
  removeAll()
  // 创建场景
  const scene = createScene(props.mapConfig)
  // 设置照相机
  const camera = createCamera(props.mapId, props.mapConfig)
  // 创建渲染器
  const renderer = createRenderer(props.mapId, props.mapConfig)
  // 创建文本渲染器
  const labelRenderer = createLabelRenderer(props.mapId, props.mapConfig)
  // 创建控制器
  const controls = createControls(camera, renderer)
  // 初始化光源
  initLights(scene, props.mapEnv)
  // 渲染元素
  const root = document.getElementById(props.mapId) as HTMLElement
  root.appendChild(renderer.domElement)
  root.appendChild(labelRenderer.domElement)

  // 设置动画
  const animate = () => {
    animateId = requestAnimationFrame(animate)
    controls.update()
    camera.lookAt(scene.position)
    // 场景旋转
    if (props.mapConfig.rotation.show) {
      if (props.mapConfig.rotation.times === 'infinite' || Math.abs(scene.rotation.y) < Math.PI * 2) {
        scene.rotation.y +=
          props.mapConfig.rotation.direction === 'n' ? props.mapConfig.rotation.speed / 1000 : -props.mapConfig.rotation.speed / 1000
      }
    }
    // 使用渲染器渲染相机看这个场景的内容渲染出来
    renderer.render(scene, camera)
    labelRenderer.render(scene, camera)
  }
  animate()

  // 监听屏幕大小改变的变化，设置渲染的尺寸
  onResize = () => {
    const container = document.getElementById(props.mapId) as HTMLElement
    const WIDTH = props.mapConfig.fullScreen ? props.mapConfig.screenWidth : container.clientWidth
    const HEIGHT = props.mapConfig.fullScreen ? props.mapConfig.screenHeight : container.clientHeight
    // 更新摄像头
    camera.aspect = WIDTH / HEIGHT
    // 更新摄像机的投影矩阵
    camera.updateProjectionMatrix()
    // 更新渲染器
    renderer.setSize(WIDTH, HEIGHT)
    // 设置渲染器的像素比例
    renderer.setPixelRatio(window.devicePixelRatio)
    labelRenderer.setSize(WIDTH, HEIGHT)
  }
  window.addEventListener('resize', onResize)

  //渲染地图
  renderMap({
    mapId: props.mapId,
    scene,
    camera,
    geoData: props.geoData,
    barData: props.barData,
    mapConfig: props.mapConfig,
    barConfig: props.barConfig,
    scatterData: props.scatterData,
    scatterConfig: props.scatterConfig,
    blockData: props.blockData,
    blockConfig: props.blockConfig,
    flyLineData: props.flyLineData,
    flyLineConfig: props.flyLineConfig,
    bgData: props.bgData,
    bgConfig: props.bgConfig,
    handleBlockClick
  })
}

onMounted(() => {
  watch(
    [() => props.bgData, () => props.geoData, () => props.blockData],
    (newVal, oldVal) => {
      init()
      props.highlightBlocks && renderHighlightBlocks(props.mapId, props.highlightBlocks, props.mapConfig)
    },
    { deep: true }
  )

  watch(
    () => props.scatterData,
    (newVal, oldVal) => {
      setTimeout(() => {
        renderScatters(props.mapId, newVal, props.scatterConfig, props.mapConfig, handleScatterHighlight)
      }, 100)
    },
    { deep: true }
  )

  watch(
    () => props.slotData,
    (newVal, oldVal) => {
      setTimeout(() => {
        renderSlots(props.mapId, newVal, props.slotConfig, props.mapConfig, props.geoData)
      }, 100)
    },
    { deep: true }
  )

  watch(
    () => props.flyLineData,
    (newVal, oldVal) => {
      setTimeout(() => {
        renderFlyLines(props.mapId, newVal, props.mapConfig, props.flyLineConfig, props.geoData)
      }, 100)
    },
    { deep: true }
  )

  watch(
    () => props.barData,
    (newVal, oldVal) => {
      setTimeout(() => {
        renderBars(props.mapId, newVal, props.barConfig, props.geoData, props.mapConfig, handleBarHighlight)
      }, 100)
    },
    { deep: true }
  )

  watch(
    () => props.highlightBlocks,
    (newVal, oldVal) => {
      setTimeout(() => {
        renderHighlightBlocks(props.mapId, props.highlightBlocks, props.mapConfig)
      }, 100)
    },
    { deep: true }
  )

  watch(
    [props.mapConfig, props.bgConfig, props.barConfig, props.scatterConfig, props.slotConfig, props.blockConfig, props.flyLineConfig, props.mapEnv],
    (newVal, oldVal) => {
      init()
      setTimeout(() => {
        props.barData && renderBars(props.mapId, props.barData, props.barConfig, props.geoData, props.mapConfig, handleBarHighlight)
        props.scatterData && renderScatters(props.mapId, props.scatterData, props.scatterConfig, props.mapConfig, handleScatterHighlight)
        props.flyLineData && renderFlyLines(props.mapId, props.flyLineData, props.mapConfig, props.flyLineConfig, props.geoData)
        props.slotData && renderSlots(props.mapId, props.slotData, props.slotConfig, props.mapConfig, props.geoData)
        props.highlightBlocks && renderHighlightBlocks(props.mapId, props.highlightBlocks, props.mapConfig)
      }, 100)
    },
    { deep: true, immediate: true }
  )
})

// 离开前
onBeforeUnmount(() => {
  // 清空所有
  removeAll()
})
</script>

<template>
  <div
    :id="props.mapId"
    class="threedmap"
    :style="{
      left: `${props.mapConfig.fullScreen && props.mapConfig.offsetX ? props.mapConfig.offsetX * 100 : 0}%`
    }"
  ></div>
</template>

<style scoped lang="less">
.threedmap {
  width: 100%;
  height: 100%;
  position: relative;
}
</style>
